package com.cupk.rotaryguard.user.service.impl;

import cn.hutool.http.useragent.UserAgent;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cupk.common.base.config.Contant;
import com.cupk.common.base.config.RedisKeyContant;
import com.cupk.common.base.utils.DateFormatUtils;
import com.cupk.common.base.utils.result.Result;
import com.cupk.common.base.utils.result.ResultGenerator;
import com.cupk.rotaryguard.user.entity.User;
import com.cupk.rotaryguard.user.entity.vo.UserVo;
import com.cupk.rotaryguard.user.entity.vo.LoginUserVo;
import com.cupk.rotaryguard.user.mapper.UserMapper;
import com.cupk.rotaryguard.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author tjy
 * @date 2023/3/16
 * @apiNote
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    @Resource
    private UserMapper userMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private final static Logger LOG = LoggerFactory.getLogger(UserServiceImpl.class);


    @Override
    public Result listUserInfo() {
        String userJson = stringRedisTemplate.opsForValue().get(RedisKeyContant.USERS_INFO);
        if (!StringUtils.isBlank(userJson)) {
            List<User> users = JSON.parseArray(userJson, User.class);
            return ResultGenerator.genSuccessResult(users, "查询成功");
        }
        List<User> users = userMapper.selectList(null);
        String usersJson = JSON.toJSONString(users);
        if (users != null) {
            stringRedisTemplate.opsForValue().set(RedisKeyContant.USERS_INFO, usersJson, RedisKeyContant.USERS_INFO_TTL, TimeUnit.MINUTES);
            return ResultGenerator.genSuccessResult(users, "查询成功");
        }
        return ResultGenerator.genFailResult(null, "查询结果为空");
    }

    /**
     * 登陆
     *
     * @param userVo  用户
     * @param request request
     * @return 返回值
     */
    @Override
    public Result login(LoginUserVo userVo, HttpServletRequest request) {
        String username = userVo.getUsername();
        String passwd = userVo.getPasswd();
        //判断用户名非空
        if (StringUtils.isEmpty(username)) {
            return ResultGenerator.genFailResult(null, "用户名不能为空");
        }
        //判断密码非空
        if (StringUtils.isEmpty(passwd)) {
            return ResultGenerator.genFailResult(null, "密码不能为空");
        }
        HttpSession session = request.getSession();
        User user;
        LocalDateTime loginTime = LocalDateTime.now();
        String loginIp = getIpAddress(request);
        LOG.info("loginTime :{} ", loginTime);
        LOG.info("loginIP :{}", loginIp);
        long loginTimeLong = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        try {
            user = userMapper.selectOne(queryWrapper);
        } catch (Exception e) {
            log.info("错误异常信息：", e);
            return ResultGenerator.genFailResult(null, e.getMessage());
        }
        if (user != null) {
            if (user.getFailCount() >= Contant.MAX_FAIL_COUNT) {
                if (user.getLastLoginTime() != null && loginTimeLong - user.getLastLoginTime().toInstant(ZoneOffset.of(Contant.OFF_SET_ID)).toEpochMilli() < Contant.INTERVAL_TIME * Contant.MULTIPLY_TIME) {
                    return ResultGenerator.genFailResult(null, "登录失败次数过多，请 " + DateFormatUtils.getString(Contant.INTERVAL_TIME * Contant.MULTIPLY_TIME - (loginTimeLong - user.getLastLoginTime().toInstant(ZoneOffset.of(Contant.OFF_SET_ID)).toEpochMilli())) + " 后再试");
                } else {
                    //如果上次出错到现在已经超过允许的重试时间了，则重新记录错误次数
                    user.setFailCount(0);
                }
            }
            user.setLastLoginTime(user.getLoginTime());
            String lastLoginIp = user.getLoginIp() == null ? null : user.getLoginIp();
            user.setLastLoginIp(lastLoginIp);
            user.setLastLoginTime(loginTime);
            if (new BCryptPasswordEncoder().matches(passwd, user.getPasswd())) {
                user.setFailCount(0);
                user.setLoginCount(user.getLoginCount() + 1);
                user.setLoginTime(loginTime);
                user.setLoginIp(loginIp);
                userMapper.updateById(user);
                UserAgent userAgent = new UserAgent();
                userAgent.getBrowser();
                JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(user));
                jsonObject.remove("passwd");
                session.setAttribute("user", user);
                return ResultGenerator.genSuccessResult(jsonObject, "登陆成功");
            } else {
                //记录用户登陆失败次数
                user.setFailCount(user.getFailCount() + 1);
                userMapper.updateById(user);
                return ResultGenerator.genFailResult(null, "用户名或密码错误");
            }
        } else {
            return ResultGenerator.genFailResult(null, "该用户不存在");
        }
    }

    /**
     * 登出
     *
     * @return 返回
     */
    @Override
    public Result logout(HttpServletRequest request) {
        request.getSession().invalidate();
        return ResultGenerator.genSuccessResult(null, "退出成功");
    }

    /**
     * 添加用户
     *
     * @param userVo 用户
     * @return 返回
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result addUser(UserVo userVo) {
        String curUsername = userVo.getUsername();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", curUsername);
        User user = userMapper.selectOne(queryWrapper);
        if (user != null) {
            return ResultGenerator.genFailResult(null, "用户名已被使用，请更换！");
        }
        User newUser = new User();
        newUser.setUsername(userVo.getUsername());
        newUser.setAddress(userVo.getAddress());
        newUser.setTel(userVo.getTel());
        newUser.setEmail(userVo.getEmail());
        newUser.setCreateTime(LocalDateTime.now());
        try {
            userMapper.insert(newUser);
        } catch (Exception e) {
            log.info("错误异常信息：", e);
            return ResultGenerator.genFailResult(null, "添加用户失败" + e.getMessage());
        }
        Set keys = stringRedisTemplate.keys(RedisKeyContant.USERS_INFO + "*");
        stringRedisTemplate.delete(keys);
        return ResultGenerator.genSuccessResult(userVo, "添加成功");
    }

    /**
     * 修改密码
     *
     * @param oldPwd 老密码
     * @param newPwd 新密码
     * @return 返回
     */
    @Override
    public Result chPwd(Long userId, String oldPwd, String newPwd) {
        User curUser = userMapper.selectById(userId);
        String trueOldPwd = curUser.getPasswd();
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if (!passwordEncoder.matches(oldPwd, trueOldPwd)) {
            return ResultGenerator.genFailResult(null, "原密码错误!");
        }
        String newNewPwd = new BCryptPasswordEncoder().encode(newPwd);
        curUser.setPasswd(newNewPwd);
        userMapper.updateById(curUser);
        return ResultGenerator.genSuccessResult(null, "修改成功");
    }

    /**
     * 修改用户名
     *
     * @param userId
     * @param username
     * @return
     */
    @Override
    public Result chUsername(Long userId, String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        User user = userMapper.selectOne(queryWrapper);
        if (user != null) {
            return ResultGenerator.genFailResult(null, "该用户名已存在");
        }
        if (username.length() >= Contant.PASSWORD_MIN_LEN && username.length() <= Contant.PASSWORD_MAX_LEN) {
            User curUser = userMapper.selectById(userId);
            curUser.setUsername(username);
            userMapper.updateById(curUser);
            Long id = curUser.getId();
            stringRedisTemplate.opsForValue().set(RedisKeyContant.USERS_INFO + ":" + id, JSON.toJSONString(curUser), RedisKeyContant.USERS_INFO_TTL, TimeUnit.MINUTES);
            return ResultGenerator.genSuccessResult(null, "修改成功");
        }
        return ResultGenerator.genFailResult(null, "参数不合法");
    }

    /**
     * 批量删除
     *
     * @param ids 主键
     * @return 返回
     */
    @Override
    public Result delUsers(List<Integer> ids) {
        userMapper.deleteBatchIds(ids);
        Set keys = stringRedisTemplate.keys(RedisKeyContant.USERS_INFO + "*");
        stringRedisTemplate.delete(keys);
        return ResultGenerator.genSuccessResult(null, "删除成功");
    }

    /**
     * 根据用户名分页查询
     *
     * @param pageNum  页号
     * @param pageSize 每页大小
     * @param username 用户名
     * @return 返回
     */
    @Override
    public Result getUsersByUsername(Integer pageNum, Integer pageSize, String username) {
        String userKeyInRedis = RedisKeyContant.USERS_INFO + ":" + pageNum + ":" + pageSize + ":" + username;
        //从redis查询
        String userJson = stringRedisTemplate.opsForValue().get(userKeyInRedis);
        if (!StringUtils.isBlank(userJson)) {
            List<User> users = JSON.parseArray(userJson, User.class);
            Page<User> page = new Page<>(pageNum, pageSize, users.size());
            page.setRecords(users);
            page.setTotal(users.size());
            return ResultGenerator.genSuccessResult(page, "查询成功!");
        }
        //redis中不存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(username)) {
            queryWrapper.like("username", username);
        }
        IPage<User> pageDate = new Page<>(pageNum, pageSize);
        IPage<User> userIPage = userMapper.selectPage(pageDate, queryWrapper);
        if (!ObjectUtils.isEmpty(userIPage)) {
            //查得到数据 存入redis
            stringRedisTemplate.opsForValue().set(userKeyInRedis, JSON.toJSONString(userIPage.getRecords()), RedisKeyContant.USERS_INFO_TTL, TimeUnit.MINUTES);
            return ResultGenerator.genSuccessResult(userIPage, "查询成功!");
        }
        return ResultGenerator.genFailResult(null, "结果为空");
    }

    /**
     * 获取被注销的用户
     *
     * @param pageNum  页码
     * @param pageSize 大小
     * @return 返回
     */
    @Override
    public Result listBannedUsers(Integer pageNum, Integer pageSize) {
        String banUserListJson = stringRedisTemplate.opsForValue().get(RedisKeyContant.USERS_INFO_BAN);
        if (StringUtils.isNotBlank(banUserListJson)) {
            List<User> users = JSON.parseArray(banUserListJson, User.class);
            return ResultGenerator.genSuccessResultPage(users, users.size());
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", 1);
        List<User> users = userMapper.selectList(queryWrapper);
        if (users != null) {
            stringRedisTemplate.opsForValue().set(RedisKeyContant.USERS_INFO_BAN, JSON.toJSONString(users), 30, TimeUnit.MINUTES);
            return ResultGenerator.genSuccessResultPage(users, users.size());
        }
        return ResultGenerator.genSuccessResult(null, "结果为空");
    }

    /**
     * 封禁账户
     *
     * @param id 主键
     * @return 返回
     */
    @Override
    public Result banUser(Integer id) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        User user = userMapper.selectOne(queryWrapper);
        int curStatus = user.getStatus();
        if (curStatus == 1) {
            return ResultGenerator.genFailResult(null, "当前用户已被禁用!");
        }
        user.setStatus(1);
        userMapper.updateById(user);
        return ResultGenerator.genSuccessResult(null, "封禁成功");
    }

    /**
     * 根据用户id查询用户信息
     *
     * @param id 用户id
     * @return 返回
     */
    @Override
    public Result getUserInfoById(Long id) {
        String userInfoJson = stringRedisTemplate.opsForValue().get(RedisKeyContant.USERS_INFO + ":" + id);
        if (StringUtils.isNotBlank(userInfoJson)) {
            User user = JSONObject.parseObject(userInfoJson, User.class);
            return ResultGenerator.genSuccessResult(user);
        }
        User user = userMapper.selectById(id);
        if (ObjectUtils.isNotEmpty(user)) {
            stringRedisTemplate.opsForValue().set(RedisKeyContant.USERS_INFO + ":" + id, JSON.toJSONString(user), RedisKeyContant.USERS_INFO_TTL, TimeUnit.MINUTES);
        }
        return ResultGenerator.genSuccessResult(user);
    }

    /**
     * 更改用户信息
     *
     * @param id 用户id
     * @return 返回
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result chUserInfo(String id, String username, String tel, String address, String email) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        User user = userMapper.selectOne(queryWrapper);
        if (ObjectUtils.isEmpty(user)) {
            return ResultGenerator.genFailResult(id, "用户id为" + id + "的用户不存在");
        }
        user.setUsername(username);
        user.setTel(tel);
        user.setAddress(address);
        user.setEmail(email);
        try {
            userMapper.updateById(user);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        Set keys = stringRedisTemplate.keys(RedisKeyContant.USERS_INFO + "*");
        stringRedisTemplate.delete(keys);
        return ResultGenerator.genSuccessResult(user, "修改成功!");
    }

    /**
     * 单个删除用户（逻辑删除）
     *
     * @param id id
     * @return 返回
     */
    @Override
    public Result delUserById(Integer id) {
        userMapper.deleteById(id);
        Set keys = stringRedisTemplate.keys(RedisKeyContant.USERS_INFO + "*");
        stringRedisTemplate.delete(keys);
        return ResultGenerator.genSuccessResult(null, "删除成功 id:" + id);
    }

    public static String getIpAddress(HttpServletRequest request) {
        String ip;
        try {
            ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || Contant.UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || Contant.UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || Contant.UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
                if (Contant.LOCAL_IP.equals(ip)) {
                    // 根据网卡取本机配置的IP
                    InetAddress iNet;
                    try {
                        iNet = InetAddress.getLocalHost();
                        ip = iNet.getHostAddress();
                    } catch (UnknownHostException e) {
                        log.info("错误异常信息：", e);
                    }
                }
            }
            // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
            if (ip != null && ip.length() > Contant.IPV4_MAX_LENGTH) {
                // = 15
                if (ip.indexOf(Contant.IP_SPLIT) > 0) {
                    ip = ip.substring(0, ip.indexOf(","));
                }
            }
        } catch (Exception e) {
            ip = "";
        }
        return ip;
    }

}